﻿using OpenTK;
using OpenTK.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NinjaGoat
{
    class Player : Entity
    {
        enum State
        {
            Flying,
            FlyingAfterDash,
            FlyingAfterJump,
            Grounded,
            Walled,
        }
        bool isFlying
        {
            get { return state == State.Flying || state == State.FlyingAfterDash || state == State.FlyingAfterJump; }
        }

        Vector2 size = Vector2.One;
        Vector2 velocity;
        State state;
        float wallStickTimer;
        bool canAirJump = false;
        int direction = 1; // +1 right, -1 left

        KeyboardState oldKeyState;

        // Player characteristics
        const float MAX_SPEED = 12;
        const float JUMP_HEIGHT = 4;
        const float WALL_JUMP_ANGLE = 25 * (float)Math.PI / 180;
        const float GROUND_JUMP_ANGLE = 25 * (float)Math.PI / 180;
        const float GRAVITY = 50;
        const float RESPONSE_TIME_GROUND = 0.06f;
        const float RESPONSE_TIME_AIR = 0.4f;
        const float WALL_TOUCH_DISTANCE = 0.001f;
        const float WALL_STICK_TIME = 0.5f;
        const float DASH_SPEED = 22;

        // Computed parameters
        static float groundDrag = (float)Math.Exp(-MyGameWindow.DT / RESPONSE_TIME_GROUND);
        static float airDrag = (float)Math.Exp(-MyGameWindow.DT / RESPONSE_TIME_AIR);
        static float groundAcc = MAX_SPEED * (1 - groundDrag) / groundDrag;
        static float airAcc = MAX_SPEED * (1 - airDrag) / airDrag;
        static float jumpVel = (float)Math.Sqrt(2 * GRAVITY * JUMP_HEIGHT);
        static float wallJumpVel = jumpVel * (float)Math.Tan(WALL_JUMP_ANGLE);
        static float groundTangentalJumpVel = jumpVel * (float)Math.Tan(GROUND_JUMP_ANGLE);

        public Player(MyGameWindow window)
            : base(window)
        {
            Scale = size;
            Position = new Vector2(5, 5);

            Texture = new Texture();
            Texture.Load("character.png");
        }

        public override void Update()
        {
            var keystate = OpenTK.Input.Keyboard.GetState();
            if (oldKeyState == null)
                oldKeyState = keystate;

            // Handle input
            float dVX = 0;
            if (keystate[Key.Left] && (state != State.Walled))
                dVX--;
            if (keystate[Key.Right] && (state != State.Walled))
                dVX++;
            if (dVX != 0)
                direction = (int)dVX;

            if (keystate[Key.A] && !oldKeyState[Key.A] && state == State.Flying)
            {
                velocity.X = direction * DASH_SPEED;
                state = State.FlyingAfterDash;
            }
            if (keystate[Key.S] && !oldKeyState[Key.S])
            {
                switch (state)
                {
                    case State.Grounded:
                        velocity.Y = jumpVel;
                        velocity.X = groundTangentalJumpVel * direction;
                        break;

                    case State.Walled:
                        velocity.Y = jumpVel;
                        velocity.X = wallJumpVel * direction;
                        break;

                    case State.FlyingAfterDash:
                        velocity.Y = jumpVel;
                        state = State.FlyingAfterJump;
                        break;
                }
            }
            velocity.X += dVX * (state == State.Grounded ? groundAcc : airAcc);

            velocity.Y -= GRAVITY * DT;

            velocity.X *= state == State.Grounded ? groundDrag : airDrag;

            // Handle collisions
            Position.X += velocity.X * DT;
            if (Window.currentLevel.Touching(Position, size))
            {
                if (velocity.X < 0)
                {
                    Position.X = (int)Position.X + 1;
                    if (state != State.Grounded)
                    {
                        direction = 1;
                        state = State.Walled;
                    }
                }
                if (velocity.X > 0)
                {
                    Position.X = (int)Position.X;
                    if (state != State.Grounded)
                    {
                        direction = -1;
                        state = State.Walled;
                    }
                }
                velocity.X = 0;
            }

            Position.Y += velocity.Y * DT;
            if (Window.currentLevel.Touching(Position, size))
            {
                if (velocity.Y < 0)
                {
                    Position.Y = (int)Position.Y + 1;
                    state = State.Grounded;
                }
                if (velocity.Y > 0)
                {
                    Position.Y = (int)Position.Y;
                }
                velocity.Y = 0;
            }
            else if(!isFlying)
            {
                state = State.Flying;
            }

            if (state != State.Grounded)
            {
                bool touchLeft = Window.currentLevel.Touching(Position - WALL_TOUCH_DISTANCE * Vector2.UnitX, size);
                bool touchRight = Window.currentLevel.Touching(Position + WALL_TOUCH_DISTANCE * Vector2.UnitX, size);

                if (touchLeft || touchRight)
                {
                    state = State.Walled;
                    direction = touchLeft ? 1 : -1;
                }
                else if(state == State.Walled)
                    state = State.Flying;
            }


            oldKeyState = keystate;

            setCamera();

            if (state == State.Walled)
            {
                this.Scale.X = -size.X * direction;
                this.Rotation = 90;
            }
            else
            {
                this.Scale.X = size.X * direction;
                this.Rotation = 0;
            }
            

        }

        void setCamera()
        {
            float distance = 3;

            if (Position.Y - Window.Renderer.cameraPosition.Y > distance)
                Window.Renderer.cameraPosition.Y = Position.Y - distance;
            if (Window.Renderer.cameraPosition.Y - Position.Y > distance)
                Window.Renderer.cameraPosition.Y = Position.Y + distance;

            if (Position.X - Window.Renderer.cameraPosition.X > distance)
                Window.Renderer.cameraPosition.X = Position.X - distance;
            if (Window.Renderer.cameraPosition.X - Position.X > distance)
                Window.Renderer.cameraPosition.X = Position.X + distance;

            //Window.Renderer.cameraPosition.X = Position.X;
        }

        public void UpdateOld()
        {
            /*
            var keystate = OpenTK.Input.Keyboard.GetState();
            if (oldKeyState == null)
                oldKeyState = keystate;

            float dVX = 0;
            if (keystate[Key.Left]) dVX -= 1;
            if (keystate[Key.Right]) dVX += 1;

            velocity.X += dVX * (state == State.Grounded ? groundAcc : airAcc);


            if (keystate[Key.A] && !oldKeyState[Key.A])
            {
                if (state == State.WalledLeft)
                    velocity.X = wallJumpVel;
                if (state == State.WalledRight)
                    velocity.X = -wallJumpVel;
                if (state != State.Flying)
                    velocity.Y = jumpVel;
                state = State.Flying;
            }
                

            velocity.Y -= DT * GRAVITY;

            if (Window.currentLevel.Touching(Position, Scale))
                throw new Exception();

            state = State.Flying;
            Position.Y += velocity.Y * DT;
            if (Window.currentLevel.Touching(Position, Scale))
            {
                if (velocity.Y < 0)
                {
                    Position.Y = (int)Position.Y + 1;
                    state = State.Grounded;
                }
                if (velocity.Y > 0)
                {
                    Position.Y = (int)Position.Y;
                }
                velocity.Y = 0;
            }
            else
            {
                Position.X -= WALL_TOUCH_DISTANCE;
                bool touchLeft = Window.currentLevel.Touching(this);
                Position.X += 2 * WALL_TOUCH_DISTANCE;
                bool touchRight = Window.currentLevel.Touching(this);
                Position.X -= WALL_TOUCH_DISTANCE;

                if (touchLeft || touchRight)
                    wallStickTimer -= DT;

                if (wallStickTimer <= 0)
                    state = State.Flying;
            }

            Position.X += velocity.X * DT;
            if (Window.currentLevel.Touching(this))
            {
                if (velocity.X < 0)
                {
                    Position.X = (int)Position.X + 1;
                    if (state == State.Flying)
                        state = State.WalledLeft;
                }
                if (velocity.X > 0)
                {
                    Position.X = (int)Position.X;
                    if (state == State.Flying)
                        state = State.WalledRight;
                }
                velocity.X = 0;
            }

            if(state == State.Grounded)
                velocity.X *= groundDrag;
            else
                velocity.X *= airDrag;*/
        }
    }
}
